package ch.ethz.smseth.service;

import java.io.IOException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.preference.PreferenceManager;
import android.util.Log;
import ch.ethz.smseth.ContactUtil;
import ch.ethz.smseth.PhoneSender;
import ch.ethz.smseth.R;
import ch.ethz.smseth.RequestSender;
import ch.ethz.smseth.SenderException;
import ch.ethz.smseth.SmsStore;
import ch.ethz.smseth.ContactUtil.Contact;
import ch.ethz.smseth.activity.FailureActivity;
import ch.ethz.smseth.activity.MainActivity;


public class SenderService extends Service
{
	public static final String EXTRA_MESSAGE = "message";
	public static final String EXTRA_DEST = "dest";
	public static final String EXTRA_ERROR = "error";
	public static final String EXTRA_METHOD = "method";
	public static final String EXTRA_NOTIFICATION_ID = "notID";
	public static final String LOG_TAG = "smsETH";
	public static final int METHOD_ETH = 0;
	public static final int METHOD_PHONE = 1;
	private volatile int _messageId;
	private SmsStore _smsStore;
	private SharedPreferences _prefs;
	
	@Override
	public IBinder onBind(Intent arg0)
	{
		return null;
	}

	@Override
	public void onCreate()
	{
		super.onCreate();
		_smsStore = new SmsStore(getContentResolver());
		_prefs = PreferenceManager.getDefaultSharedPreferences(this);
		getContentResolver();
	}
	
	@Override
	public void onStart(Intent intent, int startId)
	{
		Bundle b = intent.getExtras();
		
		String user = _prefs.getString(MainActivity.PREFS_NAME, "");
		String pw = _prefs.getString(MainActivity.PREFS_PW, "");
		String sender = _prefs.getString(MainActivity.PREFS_SENDER, "");
		Thread thread = new Thread(new Sender(user, pw, b.getString(EXTRA_MESSAGE), sender, b.getStringArray(EXTRA_DEST), b.getInt(EXTRA_METHOD, METHOD_ETH)));
		thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
		thread.start();
	}
	
	private int getTimeout()
	{
		try
		{
			String timeout = _prefs.getString(MainActivity.PREFS_TIMEOUT, "60");
			return Integer.parseInt(timeout)*1000;
		}
		catch (NumberFormatException e)
		{
			return 60000;
		}
	}
	
	private class Sender implements Runnable
	{
		private String _user;
		private String _pw;
		private String _message;
		private String _sender;
		private String[] _dest;
		private int _method;
		
		public Sender(String user, String pw, String message, String sender, String[] dest, int method)
		{
			_user = user;
			_pw = pw;
			_message = message;
			_sender = sender;
			_dest = dest;
			_method = method;
		}
		
		@Override
		public void run()
		{
			for (String dest : _dest)
			{
				int id = notifyProgress(dest);
				
				try
				{
					switch (_method)
					{
						case METHOD_ETH:
							new RequestSender().Send(_user, _pw, _message, _sender, dest, getTimeout());
							break;
						case METHOD_PHONE:
							new PhoneSender(SenderService.this).send(dest, _message);
							break;
						default:
							throw new Exception("Unknown send method: " + _method);
					}
					
					_smsStore.insertSent(dest, _message);
					notifySuccess(id, dest);
				}
				catch (SenderException e)
				{
					Uri uri = _smsStore.insertDraft(dest, _message);
					notifyError(id, "Couldn't deliver sms to " + dest + ": " + e.getMessage(), _message, dest, uri.toString());
				}
				catch (IOException e)
				{
					Uri uri = _smsStore.insertDraft(dest, _message);
					notifyError(id, "Connection error: " + e.getMessage(), _message, dest, uri.toString());
				}
				catch (Exception e)
				{
					_smsStore.insertDraft(dest, _message);
					Log.e(LOG_TAG, "Error while sending message", e);
					notifyFatalError(id);
				}	
			}
		}
	}
	
	private String getNameText(String number)
	{
		Contact result = ContactUtil.get(this).fetchContactByNumber(number);
		if (result != null) return result.name + " (" + result.label + ")";
		else return number;
	}
	
	private int notifyProgress(String dest)
	{
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
		
		int icon = android.R.drawable.stat_sys_upload;
		CharSequence tickerText = "SMS is being sent...";
		long when = System.currentTimeMillis();

		Context context = getApplicationContext();
		CharSequence contentTitle = "smsETH";
		CharSequence contentText = "Sending to " + getNameText(dest) + "...";
		
		Intent dummyIntent = new Intent();
		PendingIntent pending = PendingIntent.getActivity(this, 0, dummyIntent, 0);
		
		Notification notification = new Notification(icon, tickerText, when);
		notification.setLatestEventInfo(context, contentTitle, contentText, pending);
		notification.flags |= Notification.FLAG_ONGOING_EVENT;
		notification.flags |= Notification.FLAG_INSISTENT;
	
		int id = _messageId ++;
		
		if (_prefs.getBoolean(MainActivity.PREFS_NOTIFY_PROGRESS, true))
		{
			mNotificationManager.notify(id, notification);
		}
		return id;
	}
	
	private void notifyFatalError(int id)
	{
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
		
		int icon = R.drawable.error;
		CharSequence tickerText = "Fatal error in smsETH";
		long when = System.currentTimeMillis();

		Context context = getApplicationContext();
		CharSequence contentTitle = "smsETH";
		CharSequence contentText = "Fatal error: See log for details.";
		
		Intent dummyIntent = new Intent();
		PendingIntent pending = PendingIntent.getActivity(this, 0, dummyIntent, 0);
		
		Notification notification = new Notification(icon, tickerText, when);
		notification.setLatestEventInfo(context, contentTitle, contentText, pending);
		notification.flags |= Notification.FLAG_AUTO_CANCEL;
		
		mNotificationManager.cancel(id);
		mNotificationManager.notify(_messageId++, notification);
	}
	
	private void notifySuccess(int id, String dest)
	{
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
		
		int icon = R.drawable.ok;
		CharSequence tickerText = "SMS has been sent";
		long when = System.currentTimeMillis();

		Context context = getApplicationContext();
		CharSequence contentTitle = "smsETH";
		CharSequence contentText = "Sent to " + getNameText(dest) + ".";
		
		Intent dummyIntent = new Intent();
		PendingIntent pending = PendingIntent.getActivity(this, 0, dummyIntent, 0);
		
		Notification notification = new Notification(icon, tickerText, when);
		notification.setLatestEventInfo(context, contentTitle, contentText, pending);
		notification.flags |= Notification.FLAG_AUTO_CANCEL;
		
		mNotificationManager.cancel(id);
		int newId = _messageId++;
		if (_prefs.getBoolean(MainActivity.PREFS_NOTIFY_SENT, true))
		{
			mNotificationManager.notify(newId, notification);
			
			String removeAfter = _prefs.getString(MainActivity.PREFS_AUTOHIDE_SENT, "30");
			if (!"never".equals(removeAfter))
			{
				try
				{
					int time = Integer.parseInt(removeAfter);
					Thread thread = new Thread(new SuccessNotificationRemover(newId, time));
					thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
					thread.start();
				}
				catch (NumberFormatException e)
				{
					// should not occur
				}
			}
		}
	}
	
	private class SuccessNotificationRemover implements Runnable
	{
		private int _id;
		private int _seconds;
		
		public SuccessNotificationRemover(int id, int seconds)
		{
			_id = id;
			_seconds = seconds;
		}
		
		@Override
		public void run()
		{
			try
			{
				Thread.sleep(_seconds * 1000);
				NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				mNotificationManager.cancel(_id);
			} catch (InterruptedException e)
			{
				NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				mNotificationManager.cancel(_id);
			}
		}
		
	}
	
	private void notifyError(int id, String errMessage, String originalMessage, String originalDest, String draftUri)
	{
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
		
		int icon = R.drawable.error;
		CharSequence tickerText = "smsETH Error";
		long when = System.currentTimeMillis();

		int newId = _messageId++;
		
		Context context = getApplicationContext();
		CharSequence contentTitle = "smsETH";
		CharSequence contentText = errMessage;
		Intent notificationIntent = new Intent(this, FailureActivity.class);
		notificationIntent.putExtra(EXTRA_MESSAGE, originalMessage);
		notificationIntent.putExtra(EXTRA_DEST, originalDest);
		notificationIntent.putExtra(EXTRA_ERROR, errMessage);
		notificationIntent.putExtra(EXTRA_NOTIFICATION_ID, newId);
		notificationIntent.putExtra(FailureActivity.EXTRA_MESSAGE_URI, draftUri);
		PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
		Notification notification = new Notification(icon, tickerText, when);
		notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
		notification.defaults |= Notification.DEFAULT_VIBRATE;
		
		mNotificationManager.cancel(id);
		mNotificationManager.notify(newId, notification);
	}
}
